/***********************************************************************************************//**
*\n  @file       device_gpio.cpp
*\n  @brief      GPIO设备类
*\n  @details
*\n -----------------------------------------------------------------------------------
*\n  文件说明：
*\n       1. GPIO设备类
*\n       2. 读写gpio
*\n
*\n
*\n -----------------------------------------------------------------------------------
*\n  版本:     修改人:       修改日期:             描述:
*\n  V0.01   罗先能        2021.4.12          创建
*\n
***************************************************************************************************/

/**************************************************************************************************
* 头文件
***************************************************************************************************/
#include <iostream>
#include "device_gpio.hpp"

#undef  DEBUG_LEVEL
#define DEBUG_LEVEL  2   // 定义debug级别 (0 ~ 5)
#include "vdebug.h"

using namespace std;

/**************************************************************************************************
* 宏定义、结构定义
***************************************************************************************************/


/**************************************************************************************************
* 全局变量声明、定义
***************************************************************************************************/


/**************************************************************************************************
* 私有变量声明、定义
***************************************************************************************************/


/**************************************************************************************************
* 私有函数声明、定义
***************************************************************************************************/


/**************************************************************************************************
* 全局函数声明、定义
***************************************************************************************************/
TAxiGpio::~TAxiGpio()
{
	debug_printf("%s->~TAxiGpio()\n", this->mName);
}

/*************************************************************************************//**
*\n 函数名称： TAxiGpio.TAxiGpio()
*\n 功能描述：
*\n            构造函数， 初始化实例参数，
*\n            使能gpio时钟
*\n 输入参数：
*\n            const char name[]  : 实例名称
*\n            TBus *gpioBus      : 关联gpio端口总线
*\n 输出参数:
*\n
*\n 返 回 值:
*\n           0 成功; -1 失败; -n 错误码
*\n -----------------------------------------------------------------------------------
*\n  版本:   修改人:       修改日期:        描述:
*\n  V0.01 罗先能        2021.4.13     创建
*****************************************************************************************/
TAxiGpio::TAxiGpio(const char name[], TBus *gpioBus)
{
	// 1. 保存名称
	memset(this->mName, 0, sizeof(this->mName));
	int iSize = sizeof(this->mName) - 1;
	iSize = iSize < strlen(name) ? iSize : strlen(name);
	memcpy(this->mName, name, iSize);

	this->pBus = gpioBus;   // 保存关联总线

	// set MIO[38] = 1 (临时使用)
	// 0xe000a244	  0x00000040
	// 0xe000a248	  0x00000040
	// 0xe000a008	  0xffbf0040
	RegisterSet(pBus->pBaseAddress, 0x244, 0x00000040);
	RegisterSet(pBus->pBaseAddress, 0x248, 0x00000040);
	RegisterSet(pBus->pBaseAddress, 0x008, 0xffbf0040);
	debug_printf("TGpio::TGpio(%s,%p)\n", this->mName, gpioBus);
}



// virtual:
int  TAxiGpio::Write(long address, void * pBuf, long  iSizeToWrite)   //override
{
	if (NULL == this->pBus)
	{
		debug_error("pBus == NULL \n");
		return -1;
	}

	// 设置GPIO MIO[38]输出
	if (*(long*)pBuf == 0)
	{
		// set MIO[38] = 0
		RegisterSet(pBus->pBaseAddress, 0x244, 0x00000040);
		RegisterSet(pBus->pBaseAddress, 0x248, 0x00000040);
		RegisterSet(pBus->pBaseAddress, 0x008, 0xffbf0000);
	}
	else
	{
		// set MIO[38] = 1
		// 0xe000a244	  0x00000040
		// 0xe000a248	  0x00000040
		// 0xe000a008	  0xffbf0040
		RegisterSet(pBus->pBaseAddress, 0x244, 0x00000040);
		RegisterSet(pBus->pBaseAddress, 0x248, 0x00000040);
		RegisterSet(pBus->pBaseAddress, 0x008, 0xffbf0040);
	}

	return sizeof(long);
}



#if 1
#define SYSFS_GPIO_DIR    "/sys/class/gpio"
#define POLL_TIMEOUT      (2 * 1000)             /* 2 seconds */
#define MAX_BUF           64

// private:
int TGpioFs::Export(u32 gpio)
{
	int fd, len;
	char buf[MAX_BUF];

	fd = open(SYSFS_GPIO_DIR "/export", O_WRONLY);
	if (fd < 0)
	{
		debug_error("gpio/export");
		return fd;
	}

	len = snprintf(buf, sizeof(buf), "%d", gpio);
	write(fd, buf, len);
	close(fd);

	return 0;
}

// private:
int TGpioFs::Unexport(u32 gpio)
{
	int fd, len;
	char buf[MAX_BUF];

	fd = open(SYSFS_GPIO_DIR "/unexport", O_WRONLY);
	if (fd < 0)
	{
		debug_error("gpio/export");
		return fd;
	}

	len = snprintf(buf, sizeof(buf), "%d", gpio);
	write(fd, buf, len);
	close(fd);
	return 0;
}

// private:
int TGpioFs::SetDirection(u32 gpio, u32 out_flag)
{
	int fd, len;
	char buf[MAX_BUF];

	len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio);

	fd = open(buf, O_WRONLY);
	if (fd < 0)
	{
		debug_error("gpio/direction");
		return fd;
	}

	if (out_flag == INPUT)
		write(fd, "in", 3);
	else
		write(fd, "out", 4);

	close(fd);
	return 0;
}

// private:
int TGpioFs::SetValue(u32 gpio, u32 value)
{
	int fd, len;
	char buf[MAX_BUF];

	len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

	fd = open(buf, O_WRONLY);
	if (fd < 0)
	{
		debug_error("gpio/set-value");
		return fd;
	}

	if (value)
		write(fd, "1", 2);
	else
		write(fd, "0", 2);

	close(fd);
	return 0;
}

// private:
int TGpioFs::GetValue(u32 gpio, u32 *value)
{
	int fd, len;
	char buf[MAX_BUF];
	char ch;

	len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

	fd = open(buf, O_RDONLY);
	if (fd < 0)
	{
		debug_error("gpio/get-value");
		return fd;
	}

	read(fd, &ch, 1);

	if (ch != '0')
	{
		*value = 1;
	}
	else
	{
		*value = 0;
	}

	close(fd);
	return 0;
}

// private:
int TGpioFs::SetEdge(u32 gpio, char *edge)
{
	int fd, len;
	char buf[MAX_BUF];

	len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/edge", gpio);

	fd = open(buf, O_WRONLY);
	if (fd < 0)
	{
		debug_error("open() < 0\n");
		return fd;
	}

	write(fd, edge, strlen(edge) + 1);
	close(fd);
	return 0;
}

// private:
int TGpioFs::FdOpen(u32 gpio)
{
	int fd, len;
	char buf[MAX_BUF];

	len = snprintf(buf, sizeof(buf), SYSFS_GPIO_DIR "/gpio%d/value", gpio);

	fd = open(buf, O_RDONLY | O_NONBLOCK);
	if (fd < 0)
	{
		debug_error("open() < 0\n");
	}
	return fd;
}

// private:
int TGpioFs::FdClose(int fd)
{
	return close(fd);
}

#if 2
TGpioFs::TGpioFs(const char name[], int iNo, int direct, int defaultOut_or_edge)
{
	this->mNo = iNo + 906;
	this->mDirection = direct;

	this->Export(this->mNo);
	this->SetDirection(this->mNo, direct);
	if(direct == INPUT)
	{
		if (defaultOut_or_edge == 1)
			this->SetEdge(this->mNo, (char*)"rising");
		else if (defaultOut_or_edge == 2)
			this->SetEdge(this->mNo, (char*)"falling");
		else if (defaultOut_or_edge == 4)
		{
		}
		else if (defaultOut_or_edge == 8)
		{
		}
	}
	else // if(direct == OUTPUT)
	{
		this->SetValue(this->mNo, (u32)defaultOut_or_edge);
	}

}

TGpioFs::~TGpioFs()
{
	FdClose(this->mFd);
	Unexport(this->mNo);
}

// virtual
int TGpioFs::Open(void *pHandle)
{
	mFd =  FdOpen(this->mNo);
	return mFd;
}

// virtual
int TGpioFs::Close(void *pHandle)
{
	int ret;
	ret = FdClose(this->mFd);
	this->mFd = -1;
	return ret;
}

int TGpioFs::Read(long address, void *pBuf, long iSizeToRead)
{
	/*
	* 如果是OUTPUT口,则读取输出状态;
	* 如果是INPUT口, 则等待gpio中断;
	*/
	struct pollfd fdset[1];
	char buf[20];
	int  value = 0;

	if(this->mDirection == OUTPUT)
	{
	 	memset(buf, 0, sizeof(buf));
        read(this->mFd, buf, 10);
		if (buf[0] != '0')
		{
			value = 1;
		}
		else
		{
			value = 0;
		}
		if(pBus != NULL)
		{
			memcpy(pBuf, &value, sizeof(value));
		}
	}
	else  // 等待gpio中断
	{
		memset((void *)fdset, 0, sizeof(fdset));
		fdset[0].fd = this->mFd;
		fdset[0].events = POLLPRI;
		//int ret = poll(fdset, 1, -1);  // error solve
		int ret = poll(fdset, 1, POLL_TIMEOUT);
		if (ret < 0)
		{
			debug_error("poll() < 0 \n");
			return -1;
		}
		else if (ret == 0)
		{
			debug_printf("poll() == 0 \n");
			//printf(".");
			//fflush(stdout);
			return 0;
		}

	    if (fdset[0].revents & POLLPRI)
	    {
		    /*
			注意: 使用poll()函数，设置事件监听类型为POLLPRI和POLLERR在poll()返回后，
			使用lseek()移动到文件开头读取新的值,或者关闭它再重新打开读取新值。
			必须这样做否则poll函数会总是返回。
			*/
	        ret = lseek(this->mFd, 0, SEEK_SET);
	        if (ret == -1)
	        {
	            debug_error("lseek()<0 \n");
	            // continue;
	            return -1;
	        }

	        memset(buf, 0, sizeof(buf));
	        read(this->mFd, buf, 10);

	        /* 此时表示已经监听到中断触发了 */
	        return 1;
	    }

	}
}

// virtual
int TGpioFs::Write(long address, void *pBuf, long iSizeToWrite)
{
	this->SetValue(mNo, (u32)address);
}
#endif
#endif



